﻿#region

using AspectCore.DynamicProxy;
using SqlSugar;
using Vin.Extension.Aop.Attributes;
using Vin.Extension.Framework.Adapter;
using Vin.Extension.Framework.Attributes;
using Vin.Tool.AspNetCore.Singleton;

#endregion

namespace Vin.Extension.Framework.Aop;

/// <summary>
/// 方法需要是虚方法（virtual）或者接口方法（interface）才能被拦截
/// </summary>
public class VinFrameworkTransactionAttribute : VinBaseInterceptorAttribute
{
    public override async Task Invoke(AspectContext context, AspectDelegate next)
    {
        var client = VinApp.GetRequiredServiceByHttpContext<ISqlSugarClient>();
        if (client == null) throw new System.Exception("ISqlSugarClient 是空的，请检查是否注入了 ISqlSugarClient");
        
        // 判断是否已经开启了事务
        var haveTran = client.Ado.IsAnyTran();
        if (haveTran)
        {
            await next(context);
            return;
        }

        var adapter = GetAdapter(context);
        // 执行拦截器之前的操作
        adapter?.Before(context, next);
        
        // 开启事务
        await client.AsTenant().BeginTranAsync();
        // 执行下一个过滤器以及Action
        try
        {
            await next(context);
            await client.AsTenant().CommitTranAsync();
        
            // 执行拦截器之后的操作
            adapter?.After(context, next);
        }
        catch
        {
            await client.AsTenant().RollbackTranAsync();
        
            // 执行拦截器异常之后的操作
            adapter?.Rollback(context, next);
            throw;
        }
    }

    public virtual IVinTransactionAdapter? GetAdapter(AspectContext context)
    {
        var adapterType = context.ImplementationMethod
            .GetCustomAttributes(typeof(VinFrameworkTransactionAdapterAttribute<>), true)
            .FirstOrDefault()?.GetType().GenericTypeArguments.FirstOrDefault();

        if (adapterType == null)
        {
            adapterType = context.Implementation.GetType()
                .GetCustomAttributes(typeof(VinFrameworkTransactionAdapterAttribute<>), true)
                .FirstOrDefault()?.GetType().GenericTypeArguments.FirstOrDefault();
        }

        if (adapterType != null)
        {
            return VinApp.GetRequiredServiceByHttpContext(adapterType) as IVinTransactionAdapter;
        }

        return null;
    }
}

public class VinFrameworkTransactionAttribute<T> : VinFrameworkTransactionAttribute where T : IVinTransactionAdapter
{
    public override IVinTransactionAdapter? GetAdapter(AspectContext context)
    {
        return VinApp.GetServiceByHttpContext<T>();
    }
}